package FeatureEnrichment;


import java.security.InvalidAlgorithmParameterException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


import Definitions.GraphClass;
import Definitions.NodeClass;
import Global.ConstantVariable;
import Sampling.SamplingAbstractClass;
import Utility.FileUtilityClass;
import Utility.WorkerUtilityClass;






public class FeatureEnrichment 
{
	private double[][] originalAttributes;
	private int contentType;
	private int linkType;
	private int degree;
	public FeatureEnrichment() {
		this(0, 0, 1);
	}
	public FeatureEnrichment(int contentType, int linkType, int degree) {
		this.contentType = contentType;
		this.linkType = linkType;
		this.degree = degree;


	}


	public class LOGICAL_OPERATIONS
	{
		//string olabilir
		public static final int PLAIN = 0;
		public static final int NOT = 1;
		public static final int LOGICAL_AND= 2;
		public static final int LOGICAL_OR= 3;
		public static final int COUNT = 4; 
	}
	public void setOriginalAttributes(GraphClass graph) {
		this.originalAttributes = new double[graph.getDataSet().size()][graph.getDataSet().get(0).getContentList().get(this.contentType).getAttributeList().size()];
		for(int i=0; i< graph.getDataSet().size(); i++) {


			List <Double> attributes = graph.getDataSet().get(i).getContentList().get(this.contentType).getAttributeList();
			for(int j=0; j<attributes.size(); j++ ) {
				this.originalAttributes[i][j] = attributes.get(j);
			}
		}
	}
	public void setAttributes (GraphClass graph, SamplingAbstractClass sampling) {
		int [] operationList = getOperationList();
		if(operationList == null) 
			return;
		double[][] allAppendedFeatures = getAppendedFeatures(graph, sampling, operationList);
		for(int i=0 ; i<graph.getDataSet().size() ; i++) {
			graph.getDataSet().get(i).getContentList().get(this.contentType).getAttributeList().clear();
			for(int j= 0 ; j<allAppendedFeatures[0].length;j++) {
				graph.getDataSet().get(i).getContentList().get(this.contentType).getAttributeList().add(allAppendedFeatures[i][j]);
			}
		} 
	}
	private int[] getOperationList() {
		int[] operationList = null;
		String selected = ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.selected;


		if( selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.PLAIN)) {
			operationList = new int[1];
			operationList[0] = LOGICAL_OPERATIONS.PLAIN;
		}
		if( selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.COUNT)) {
			operationList = new int[1];
			operationList[0] = LOGICAL_OPERATIONS.COUNT;
		}
		if( selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.OR)) {
			operationList = new int[1];
			operationList[0] = LOGICAL_OPERATIONS.LOGICAL_OR;
		}
		if( selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.PLAIN_COUNT_OR)) {
			operationList = new int[3];
			operationList[0] = LOGICAL_OPERATIONS.PLAIN;
			operationList[1] = LOGICAL_OPERATIONS.COUNT;
			operationList[2] = LOGICAL_OPERATIONS.LOGICAL_OR;
		}
		if(selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.COUNT_AND)) {
			operationList = new int[2];
			operationList[0] = LOGICAL_OPERATIONS.COUNT;
			operationList[1] = LOGICAL_OPERATIONS.LOGICAL_AND;


		} else if(selected.equals(ConstantVariable.FEATURE_ENRICHMENT_PROPERTIES.COUNT_OR_AND)) {
			operationList = new int[3];
			operationList[0] = LOGICAL_OPERATIONS.COUNT;
			operationList[1] = LOGICAL_OPERATIONS.LOGICAL_OR;
			operationList[2] = LOGICAL_OPERATIONS.LOGICAL_AND;
		} 
		return operationList;
	}
	/*
	 * List describes appended features logical order order and
	 * list:
	 *  1: Only real features
	 *  2: Count of neighbors
	 *  3: AND 
	 *  4: OR
	 * */


	public double[][] getAppendedFeatures(GraphClass graph, SamplingAbstractClass sampling, int[] operationList) {
		try {
			int sizeOfAppendedFeatures =(operationList.length)*(originalAttributes[0].length);
			double[][] appendedFeaturesOfGraph =new double[graph.getDataSet().size()][sizeOfAppendedFeatures];
			for(int k=0; k<graph.getDataSet().size(); k++) {
				/*set specified node attributes
				 * */
				if(!sampling.isTheNodeInUnusedForThisFold(graph.getDataSet().get(k)))
				{
					appendedFeaturesOfGraph[k]= getAppendedFeaturesOfSpecifiedNode(graph.getDataSet().get(k), sampling, operationList);
				}
			}
			return appendedFeaturesOfGraph;
		} catch (Exception e ) {
			e.printStackTrace();
			throw new FeatureEnrichmentException("Feature enrichment exception thrown with error:" + e.getMessage());
		} 
	}
	public double[] getAppendedFeaturesOfSpecifiedNode(NodeClass u, SamplingAbstractClass sampling, int[] operationList) throws Exception {
		int sizeOfAppendedFeatures =(operationList.length)*(originalAttributes[0].length);


		double[] appendedFeaturesOfNode = new double[sizeOfAppendedFeatures];
		int k=0;
		for(int operationType: operationList) {
			double[] result = getSumOfNeigboursFeaturesWithSelectedLogicalOperation(u, sampling, operationType);
			for(double i: result) {
				appendedFeaturesOfNode[k]=i;
				k++;
			} 
		}
		return appendedFeaturesOfNode;
	}


	/**/
	public double[] getSumOfNeigboursFeaturesWithSelectedLogicalOperation (NodeClass u, SamplingAbstractClass sampling, int logicalOperation) throws Exception {
		//
		try {
			int order = u.getOrder();
			ArrayList<NodeClass> neighbors= u.getNeighbours(this.linkType, this.degree);
			double[] myFeatures = originalAttributes[order];
			double[] myNeigborsFeatures;
			//fill with 0
			double [] sum= new double[myFeatures.length];
			Arrays.fill(sum, 0);
			double [] temp= new double[myFeatures.length];
			if(logicalOperation == LOGICAL_OPERATIONS.PLAIN) {
				return myFeatures;
			}
			//change order...
			for(NodeClass v: neighbors) {
				if(!sampling.isTheNodeInUnusedForThisFold(v)){ 
					myNeigborsFeatures = originalAttributes[v.getOrder()];
					if(logicalOperation == LOGICAL_OPERATIONS.LOGICAL_AND) {
						temp= WorkerUtilityClass.GetLogicalAndForGivenVector(myFeatures, myNeigborsFeatures);
					} else if(logicalOperation== LOGICAL_OPERATIONS.LOGICAL_OR) {
						temp= WorkerUtilityClass.GetLogicalOrForGivenVector (myFeatures, myNeigborsFeatures);
					} else if(logicalOperation== LOGICAL_OPERATIONS.COUNT) { 
						for(int k=0; k<myNeigborsFeatures.length;k++) {
							temp[k] =myNeigborsFeatures[k];
						}
					} else {
						throw new InvalidAlgorithmParameterException("Invalid logical operaiton");
					}
					sum = WorkerUtilityClass.getVectoralSumOfTheDoubleArrays(sum,temp);
				}
			}


			return sum;
		} catch (Exception e) {
			throw new FeatureEnrichmentException("Feature Entichment exception will be thrown ... " + e.getMessage());
		}
	}


	public void printEnrichedFeaturesToFile (GraphClass graph, SamplingAbstractClass sampling, int contentType, int linkType, int degree, String FileName) {
		int[] operationList = new int[4];
		operationList[3] = LOGICAL_OPERATIONS.PLAIN;
		operationList[0] = LOGICAL_OPERATIONS.COUNT;
		operationList[1] = LOGICAL_OPERATIONS.LOGICAL_AND;
		operationList[2] = LOGICAL_OPERATIONS.LOGICAL_OR;
		double[][] AllAppendedFeatures = getAppendedFeatures(graph, sampling, operationList);
		FileUtilityClass.WriteMatrixToFile(graph,AllAppendedFeatures,"ALL_"+FileName);
	}
}


